<?php
/**
 * PrynLib
 * @copyright 2007-2009 Prynel
 * @author Olivier ROGER <roger.olivier@gmail.com>
 * @category libPrynel
 * @package File
 * @version $Revision: 96 $
 */

/**
 * Gestion de dossier
 * @category libPrynel
 * @package File
 * @version 1.2.1 
 * @author Olivier ROGER <roger.olivier@gmail.com>
 * @copyright  2007-2009 Prynel
 *       
 *
 */

class File_FolderManager
{
	private $dossier;
	private $dossierRecur;
	private $listeFichier;
	private $listeDossier;
	private $filtre;
	
	/**
	 * Constructeur
	 *
	 * @param string $path Dossier
	 */
	public function __construct($path)
	{
		$this->dossier 		= $path;
		if(!is_dir($this->dossier))
			throw new Util_ExceptionHandler('Le dossier recherché ne semble pas exister');
		$this->dossierRecur = $this->dossier;
		$this->listeFichier = array();
		$this->folderTree = array();
		$this->fileTree   = array();
		$this->tree = '';
		$this->filtre = array();
	}
	
	/**
	 * Liste les fichiers du dossier en fonctions des filtres
	 * 
	 * @param boolean $exactFilter Filtre sur le nom exact ou sur une partie
	 * @param boolean $humanSize Si true taille retournée sous forme lisible (5Mo) sinon en Ko
	 * @return array
	 */
	public function listFile($exactFilter=true,$humanSize=false)
	{
		$folder = new DirectoryIterator($this->dossier);
		$compteur =  0;
		foreach($folder as $file)
		{
			if(!$file->isDot() && !$file->isDir() && $this->filter($file->getFilename(),$exactFilter))
			{
				$this->listeFichier[$compteur]['name'] = $file->getFilename();
				if($humanSize)
				{
					$taille = $file->getSize();
					$this->listeFichier[$compteur]['size'] = File_Util::getHumanSize($taille);
				}
				else
					$this->listeFichier[$compteur]['size'] = round($file->getSize()/1024,3);
				$this->listeFichier[$compteur]['type'] = File_Util::getExtension($this->listeFichier[$compteur]['name']);
				$compteur++;
			}
		}
		sort($this->listeFichier);
		return $this->listeFichier;
	}
	
	/**
	 * Liste récursivement un dossier
	 *
	 * @since 1.1.0
	 * @return array
	 */
	public function listRecursive()
	{
		$dossier = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->dossier),true);
		$liste = array();
		foreach($dossier as $file)
		{
			$liste[$compteur]['name'] = $file->getFilename();
			$liste[$compteur]['size'] = round($file->getSize()/1024,3);
			$liste[$compteur]['depth'] = $dossier->getDepth();
			$compteur++;
			//echo str_repeat('-',$dossier->getDepth()).' '.$file.'<br />';
		}
		return $liste;
	}
	
	/**
	 * Récupère les x derniers fichiers modifiés dans un dossier
	 *
	 * @param int $nbFile Nombre de fichier à récupérer
	 * @param boolean $humanSize Retourne la date en octet ou sous forme lisible
	 * @since 1.1.9
	 * @return array
	 */
	public  function getLastFiles($nbFile=5,$humanSize = false)
	{
		$dossier 	= new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->dossier),true);
		$liste 		= array();
		$compteur 	= 0;
		foreach($dossier as $file)
		{
			if(!$file->isDir())
			{
				$taille = count($liste);
				if($taille<$nbFile)
				{
					$liste[$compteur]['name'] = $file->getFilename();
					$liste[$compteur]['date'] = $file->getMTime();
					$liste[$compteur]['path'] = $file->getPathName();
					if($humanSize)
					{
						$size = $file->getSize();
						$liste[$compteur]['size'] = File_Util::getHumanSize($size);
					}
					else
					{
						$liste[$compteur]['size'] = round($file->getSize()/1024,3);
					}
				}
				else
				{
					$lastDate 		= 0;
					$smallestIndex 	= 0;
					$smallestDate	= 0;
					$currentDate	= $file->getMTime();
					//Recherche de l'index avec la date la plus petite
					for($i=0;$i<$taille;$i++)
					{
						if($lastDate>$liste[$i]['date'])
						{
							$smallestIndex 	= $i;
							$smallestDate	= $liste[$i]['date'];
						}
						$lastDate = $liste[$i]['date'];
					}
					//Remplacement de l'index si la date du fichier courrant est > à la plus petite
					if($currentDate>$smallestDate)
					{
						$liste[$smallestIndex]['name'] = $file->getFilename();
						if($humanSize)
						{
							$size = $file->getSize();
							$liste[$smallestIndex]['size'] = File_Util::getHumanSize($size);
						}
						else
						{
							$liste[$smallestIndex]['size'] = round($file->getSize()/1024,3);
						}
						$liste[$smallestIndex]['date'] = $currentDate;
						$liste[$smallestIndex]['path'] = $file->getPathName();
					}
				}
				$compteur++;
				//echo str_repeat('-',$dossier->getDepth()).' '.$file.'<br />';
			}
		}
		//tri par date décroissante
		function triParDate($a,$b)
		{
			if($a['date']==$b['date'])
				return 0;
			return ($a['date']>$b['date'])?-1:1;
		}
		usort($liste,"triParDate");
		return $liste;
	}
	
	/**
	 * Calcul la taille occupé par un dossier et tous ses sous dossiers
	 *
	 * @since 1.1.0
	 * @return float
	 */
	public function getSize()
	{
		$dossier = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->dossier),true);
		$size = 0;
		foreach($dossier as $file)
			if(!$file->isDir())
				$size+=round($file->getSize()/1024,3);
			
		return $size;
	}
	
	/**
	 * Calcul la taille occupé par un dossier et tous ses sous dossiers. Version static
	 *
	 * @since 1.1.6
	 * @param Dossier à mesurer
	 * @static 
	 * @return float
	 */
	public static function getSizeStatic($folder)
	{
		if(!is_dir($folder))
			throw new Util_ExceptionHandler('Le dossier ne semble pas exister');
		$dossier = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($folder),true);
		$size = 0;
		foreach($dossier as $file)
			if(!$file->isDir())
				$size+=round($file->getSize()/1024,3);
			
		return $size;
	}
	
	/**
	 * Liste les dossiers présents
	 * @since 1.0.5
	 * @return unknown
	 */
	public function liste()
	{
		$folder = new DirectoryIterator($this->dossier);
		$compteur =  0;
		foreach($folder as $file)
		{
			if(!$file->isDot() && $file->isDir())
			{
				$this->listeDossier[$compteur] = $file->getFilename();
				$compteur++;
			}
		}
		return $this->listeDossier;
	}
	
	/**
	 * Ajoute un ou plusieurs filtres
	 *
	 * @param mixed $filter Filtres à lister
	 */
	public function setFilter($filter)
	{
		if(!is_array($filter))
		{
			$this->filtre[] = $filter;
		}
		else
		{
			$this->filtre = array_merge($this->filtre,$filter);
		}
	}
	
	/**
	 * Supprime un filtre
	 *
	 * @param string $filter
	 */
	public function rmFilter($filter)
	{
		$cle = array_search($filter,$this->filtre);
		if($cle!=FALSE)
			unset($this->filtre[$cle]);
		else
			throw new Util_ExceptionHandler('Le filtre demandé n\'existe pas');
	}
	
	/**
	 * Crée un dossier
	 *
	 * @param string $newDir Chemin vers le dossier
	 * @param int $chmod Chmod en octal
	 */
	public static function create($newDir,$chmod=0644)
	{
		if(!is_file($newDir))
			@mkdir(Util_String::cleantitre($newDir),$chmod);
	}
	
	/**
	 * Supprime un dossier et tout son contenu
	 *
	 * @param string $dir Dossier à supprimer
	 */
	public function remove($dir='')
	{
		if($dir=='')
			$dir = $this->dossier;
			
		$folder = new RecursiveDirectoryIterator($dir);
		while($folder->valid())
		{
			if($folder->isDir() && !$folder->isDot())
			{
				if($folder->hasChildren())
					self::remove($folder->getPathName());

			}
			elseif($folder->isFile() && !$folder->isDot())
			{
				unlink($folder->getPathname());
			}
			$folder->next();
		}
		$folder = null; // Libère l'itérateur sinon erreur d'accès lors de la suppression
		rmdir($dir);
		//echo "rmdir($dir)<br />";
	}
	
	/**
	 * Supprime uniquement les fichiers présents dans un dossier
	 * @since 1.2.0
	 */
	public function removeFiles()
	{
	
		$folder = new RecursiveDirectoryIterator($this->dossier);
		while($folder->valid())
		{
			if($folder->isFile() && !$folder->isDot() && $this->filter($folder->getFilename()))
			{
				unlink($folder->getPathname());
			}
			$folder->next();
		}
		$folder = null; // Libère l'itérateur sinon erreur d'accès lors de la suppression
	}
	
	/**
	 * Copie du contenu d'un dossier dans un autre
	 *
	 * @since 1.1.5
	 * @todo Proposer en option la copie du dossier et non pas que du contenu
	 * @param string $dest Dossier de destination
	 */
	public function copy($dest,$dir='')
	{
		if(empty($dest))
			throw new Util_ExceptionHandler('Le dossier de destination ne peut être vide');
		if(!is_dir($dest))
			self::create($dest,0777);
		
		if(empty($dir))
			$dir = $this->dossier;
				
		$folder = new RecursiveDirectoryIterator($dir);
		while($folder->valid())
		{
			if($folder->isDir() && !$folder->isDot())
			{
				if($folder->hasChildren())
				{
					$tmpPath = $dest.$folder->getFileName();
					if(!is_dir($tmpPath))
						self::create($tmpPath,0777);
					$this->copy($dest,$folder->getPathname());
				}
			}
			elseif($folder->isFile() && !$folder->isDot())
			{
				$src = $folder->getPathname();
				//Suppression du nom du dossier source.
				$pos = stripos($src,DIRECTORY_SEPARATOR);
				copy($src,$dest.substr($src,$pos+1));
			}
			$folder->next();
		}
	}
	
	/**
	 * Vérifie le fichier par rapport aux filtres
	 *
	 * @param string $nom Fichier à tester
	 * @param boolean $exact Test le nom entier ou juste une partie
	 * @return boolean True = listage autorisé
	 */
	private function filter($nom,$exact=false)
	{
		if(count($this->filtre)==0)
		{
			return true;
		}
		else
		{
			$str_filtre = implode('|',$this->filtre);
			if($exact)
				if(preg_match('`\.('.$str_filtre.')$`i',$nom))
					return true;
				else
					return false;
			else
				if(preg_match("/$str_filtre/",$nom)) // Permet de passer une regex en filtre
					return true;
				else
					return false;
		}
	}
}
 ?>